#pragma once
#include "stdafx.h"
#include "Render.h"
#include "GeneticAlgorithm.h"
#include "MapReader.h"

using namespace PathFinder;
using std::string;
using std::pair;

Renderer::Renderer()
:hasPath(false),
map_(new Map),
path_(new Path),
penColor_( RGB( 200, 0, 0) )
{
    linePen_       = ::CreatePen( PS_SOLID, LINE_WIDTH, RGB(0, 120, 100) );
    freeGridBrush_ = (HBRUSH)::GetStockObject(WHITE_BRUSH);
    barrierBrush_  = (HBRUSH)::GetStockObject(BLACK_BRUSH);
    posBrush_      = (HBRUSH)::CreateSolidBrush( RGB(200, 0, 0) );
    pathGridBrush_ = (HBRUSH)::CreateSolidBrush( RGB(192, 192, 192) );
}

Renderer::~Renderer()
{
    DeleteObject( posBrush_ );
    DeleteObject( linePen_ );
    delete map_;
    delete path_;
}


void Renderer::SetMap(const Map& map)
{
    *map_ = map;
}

void Renderer::Render(HDC surface)
{
    HBRUSH oldBrush = (HBRUSH)::SelectObject(surface, freeGridBrush_);
    HPEN   oldPen   = (HPEN)::SelectObject(surface, linePen_ );
    
    // Render line boundary
    for(int i = 0; i < map_->rowLen + 1; i++)
    {
        ::MoveToEx(surface, i * (RECT_WIDTH + LINE_WIDTH), 0, NULL);
        ::LineTo(surface, i * (RECT_WIDTH + LINE_WIDTH), RenderAreaSize().second - 1);
    }   
    for(int i = 0; i < map_->rowNum + 1; i++)
    {
        ::MoveToEx(surface, 0, i * (LINE_WIDTH + RECT_HEIGHT), NULL);
        ::LineTo(surface, RenderAreaSize().first - 1, i * (LINE_WIDTH + RECT_HEIGHT));
    }
    
    // Render real map and path, free grid, and barrier grid
    ::SelectObject(surface, barrierBrush_);
    for(int i = 0; i < map_->rowLen; i++)
        for( int j = 0; j < map_->rowNum; j++)
        {
            if(map_->grid[j][i])
                RenderGrid(surface, i, j);
        }
        
    // Render free map
    ::SelectObject(surface, freeGridBrush_);
    for(int i = 0; i < map_->rowLen; i++)
        for( int j = 0; j < map_->rowNum; j++)
        {
            if( !(map_->grid[j][i]) )
                RenderGrid(surface, i, j);
        }    
    
    // Render path
    // TODO:
    if( hasPath )
    ::SelectObject(surface, pathGridBrush_ );
    for(size_t i = 0; i < path_->size(); i++)
        RenderGrid(surface, path_->at(i).first, path_->at(i).second );
    
    ::SelectObject(surface, posBrush_);
    RenderGrid(surface, map_->initPos.first, map_->initPos.second);
    RenderGrid(surface, map_->dstPos.first , map_->dstPos.second );
    
    //restore the original brush
    ::SelectObject(surface, oldBrush);
    //and pen
    ::SelectObject(surface, oldPen);
}

void Renderer::RenderGrid(HDC surface, int x, int y)
{
    int left   = (x + 1)*LINE_WIDTH + x*RECT_WIDTH;
    int top    = (y + 1)*LINE_WIDTH + y*RECT_HEIGHT;
    int right  = (x + 1)*LINE_WIDTH + (x + 1)*RECT_WIDTH;
    int bottom = (y + 1)*LINE_WIDTH + (y + 1)*RECT_HEIGHT;
    ::Rectangle(surface, left, top, right, bottom);
}

void Renderer::RenderPath(HDC surface, const Path& path)
{
    HBRUSH oldBrush = (HBRUSH)::SelectObject(surface, pathGridBrush_);
    for( size_t i = 0; i < path.size(); ++i)
        RenderGrid(surface, path[i].first, path[i].second );
        
    ::SelectObject(surface, posBrush_);
    RenderGrid(surface, path[0].first, path[0].second);
    RenderGrid(surface, path.back().first , path.back().second );
    
    ::SelectObject(surface, oldBrush);
}

void Renderer::SetPath(const Path& path)
{
    *path_  = path;
    hasPath = true;
}

std::pair<int, int> Renderer::RenderAreaSize()
{
    int width = WindowSize().first;
    int height = WindowSize().second - TEXT_HEIGHT;
    return pair<int, int>(width, height);
}

std::pair<int, int> Renderer::WindowSize()
{
    int width  = map_->rowLen * RECT_WIDTH + (map_->rowLen + 1) * LINE_WIDTH ;
    int height = map_->rowNum * RECT_HEIGHT + (map_->rowNum + 1) * LINE_WIDTH + TEXT_HEIGHT;
    return std::pair<int ,int>(width, height);
}

void Renderer::ShowPrompt(HDC surface, const string& prompt)
{
    ::RECT rect;
    const int border = 4;
    rect.left   = border;
    rect.right  = RenderAreaSize().first - border;
    rect.top    = RenderAreaSize().second + border;
    rect.bottom = RenderAreaSize().second + TEXT_HEIGHT - border;
    ::DrawTextA(surface, prompt.c_str(), (int)prompt.length(), &rect, DT_LEFT);
}